# coding=utf8

import numpy as np


class Preliminary:

    @staticmethod
    def demo_describe_stat_fun():
        a = np.arange(9).reshape(3, 3)
        print(
            f"""
>>> a
{a}
>>> a.std()
{a.std()}
>>> a.mean()
{a.mean()}
>>> a.max()
{a.max()}
>>> a.min()
{a.min()}
>>>np.median(a)
{np.median(a)}
        """
        )

    @staticmethod
    def demo_bincount():
        """
        bincount用于计算一个整数序列seq中数字的计数
        bin的含义是数据箱，在该函数中为列表[0, ..., max(seq)]
        输出结果为输入序列中每个数据的计数：[count(x) for x in bin]
        count(x)为输入序列seq中包含x的个数
        """
        a = [np.random.randint(0, 5) for _ in range(10)]
        bc = np.bincount(a)
        print(">>> a")
        print(a)
        print(">>> bc = np.bincount(a)\n>>> bc")
        print(bc)
        print("# 其含义是，列表中每个值,为其序号值在a中的计数：\n{}".format({k: kc for k, kc in enumerate(bc)}))
        print("# 如，bc[0]={}， 说明在a中有{}个0".format(bc[1], bc[1]))

    @staticmethod
    def percentile():
        a = np.arange(12).reshape((4, 3))
        np.random.seed(100)
        np.random.shuffle(a)
        print(
            ">>> a\n"
            f"{a}\n"
            f"# 求百分位为50的百分数, 缺省情况下数组展平位一维进行计算\n"
            f">>> np.percentile(a, 50)\n"
            f"{np.percentile(a, 50)}\n"
            f"# 设定轴向为axis=0进行计算\n"
            f">>> np.percentile(a, 50, axis=0)\n"
            f"{np.percentile(a, 50, axis=0)}\n"
            f">>> np.percentile(a, 50, axis=0, keepdims=True)\n"
            f"{np.percentile(a, 50, axis=0, keepdims=True)}\n"
            f"# 求多个百分位的百分位数\n"
            f">>> np.percentile(a, range(10, 101, 20), axis=0)\n"
            f"{np.percentile(a, range(10, 101, 20), axis=0)}\n"
            f"# 设置插值方式为midpoint\n"
            f">>> np.percentile(a, range(10, 101, 20), axis=0, interpolation='midpoint')\n"
            f"{np.percentile(a, range(10, 101, 20), axis=0, interpolation='midpoint')}\n"
        )

    @staticmethod
    def percentile_plt():
        import matplotlib.pyplot as plt

        a = np.arange(4)
        p = np.linspace(0, 100, 6001)
        ax = plt.gca()
        lines = [
            ('linear', None),
            ('higher', '--'),
            ('lower', '--'),
            ('nearest', '-.'),
            ('midpoint', '-.'),
        ]
        for interpolation, style in lines:
            ax.plot(
                p, np.percentile(a, p, interpolation=interpolation),
                label=interpolation, linestyle=style)
        ax.set(
            title='Interpolation methods for list: ' + str(a),
            xlabel='Percentile',
            ylabel='List item returned',
            yticks=a)
        ax.legend()
        plt.show()

    @staticmethod
    def user_mean1(ary, axis=0):
        return ary.sum(axis=axis) / ary.shape[axis]

    @staticmethod
    def user_mean2(ary, axis=0):
        _axis = axis if axis >= 0 else ary.ndim-1
        _ary = ary.copy()
        for j in range(_axis):
            _ary = _ary.swapaxes(_axis-j-1, _axis-j)
        result = np.zeros(_ary[0, ...].shape)
        for j in range(_ary.shape[0]):
            result += _ary[j, ...]
        result = result/_ary.shape[0]
        return result

    @staticmethod
    def user_mean3(ary):
        _sum = 0
        for x in ary:
            _sum += x
        return _sum / len(ary)


def task():
    """
    使用Numpy库函数random.randint，随机生成100个考生年龄在12-16岁之间的数据，
    进行描述性统计计算，包括计数、均值、中位数、最大值、最小值、方差和标准差的计算。
    """

    data = np.random.randint(12, 16, size=100)
    print(data)
    # print(
    #     ">>> np.bincount(stu_ages)\n"
    #     "计算各个年龄值的频数：\n"
    #     f"{np.bincount(data)[12:]}\n"
    # )

    stat_funs = [np.size, np.mean, np.max, np.min, np.median, np.var, np.std]
    result = [f(data) for f in stat_funs]
    print("计算均值、最大值、最小值、中位数、方差及标准差：")
    for f, r in zip(stat_funs, result):
        print('{:6s}: {:.4f}'.format(f.__name__, r))


def training1():
    """
    随机生成100个在60-90岁之间的浮点数据，服从正态分布
    使用quantile计算分位值对应分位数，
    输出计算的分位数。
    """
    print("生成60-90之间100个服从正态分布的随机数据：")
    stu_weights = [np.random.normal(75, 5) for _ in range(100)]
    print(stu_weights[0], '...', stu_weights[-1])
    qs = [0.2, 0.4, 0.5, 0.6]
    print(np.quantile(stu_weights, q=qs))


def training1b():
    """
    随机生成100个考生年龄在12-16岁之间的浮点数据，
    使用histogram设定年龄区间[12, 14, 15, 16]进行频数计算，
    输出使用正则化的频数计算情况。
    """
    print("生成12-16之间100个随机数据：")
    stu_ages = np.random.random(100)*4 + 12
    print(stu_ages)

    print("计算指定区间[12, 14, 15, 16]的频数：")
    hist, bins = np.histogram(stu_ages, bins=[12, 14, 15, 16])
    print("hist:{}".format(hist))
    for i, x in enumerate(bins[:-1]):
        print("bin:[{}, {}) count={}".format(bins[i], bins[i+1], hist[i]))

    print("调整density=True，计算分区间正则化频数：")
    hist, bins = np.histogram(stu_ages, bins=[12, 14, 15, 16], density=True)
    print("hist:{}".format(hist))
    for i, x in enumerate(bins[:-1]):
        print("bin:[{}, {}) count={}".format(bins[i], bins[i+1], hist[i]))


def training2():
    """
    使用percentile设置百分位为:[10，20，30，40，50，60，70，80，90]，
    计算应用的各个百分位数。设置不同的插值方式，输出计算结果。
    """
    print("生成12-16之间100个随机数据：")
    stu_ages = np.random.randint(12, 16, size=100)
    print(stu_ages)

    print("计算指定百分位[10, 20, ..., 90]的百分位数：")
    q = [10+p for p in range(0, 90, 10)]
    out = np.percentile(stu_ages, q=q)
    for qv, pv in zip(q, out):
        print("percent-quantile:{} --> percentile:{}".format(qv, pv))


def test_user_mean():
    a = np.arange(64).reshape((2, 2, 4, 4))
    r1 = Preliminary.user_mean1(a, axis=2)
    r2 = Preliminary.user_mean2(a, axis=2)
    r3 = np.mean(a, axis=2)
    print(r1, '\n')
    print(r2, '\n', np.all(r1 == r3), np.all(r2 == r3))
    print(Preliminary.user_mean2(np.arange(10).reshape((2, 5)), axis=1))


if __name__ == "__main__":
    # Preliminary.demo_describe_stat_fun()
    # Preliminary.demo_bincount()
    # Preliminary.percentile()
    # Preliminary.convert_dtypes()
    # task()
    training1()
    # training2()
